package jdk.utils;

import org.junit.Test;

import java.util.concurrent.ConcurrentHashMap;

/**
 * 以下是JDK 1.7的实现方式：
 *
 *    ConcurrentHashMap采用了非常精妙的"分段锁"策略，ConcurrentHashMap的主干是个Segment数组。
 *    本质是有两层数组：  Segment[] 数组存储的是Segment对象，可以理解为小型的HashMap,因为Segment类里面有Map.Entry<k,v>[]数组，
 *
 *    Segment 继承了ReentrantLock,说明每个Segment都有可重入锁，可以执行segment.lock() 和segment.unlock()方法，实现每个segment对象的关锁和解锁功能，实现线程的安全性。
 *    当concurrentHashMap.put()方法时
 *                  1.先根据key,算出对应的Segment数组下标，index
 *                  2.获取index位置的锁，segment[index].lock()
 *                  3.生成一个Entry对象，entry=new Entry(key,value,hashcode);
 *                  4.添加到Segment对象内部的数组或者链表上，和HashMap的Put方法类似
 *                  5.释放index位置的锁，segment[index].unlock()
 *    Segment源码如下：
 *        static class Segment<K,V> extends ReentrantLock implements Serializable {
 *            private static final long serialVersionUID = 2249069246763182397L;
 *
 *            final float loadFactor; //加载因子
 *
 *            int threshold;  //最大存储数据容量
 *
 *            HashEntry<K,V>[]  tab ;  //HashEntry对象
 *
 *            Segment(float lf,int threshold,HashEntry<K,V>[] tab) {
 *
 *               this.loadFactor = lf;
 *               this.threshold=threshold;
 *               this.tab=tab;
 *            }
 *        }
 *
 *
 *
 *    1.ConcurrentHashMap是怎么扩容的，最小的初始化容量是多少。
 *    首先ConcurrentHashMap不直接存储HashEntry键值对节点，而是里面有个Segment数组，Segment数组的大小其实就是并发的大小，每个Segment对象里面持有HashEntry[]
 *    数组，所以ConcurrentHashMap的大小就是每个Segment[]数组里面持有的HashEnry[]的和，每个Segment对象持有的HashEntry[]是4，而ConcurrentHashMap持有的Segment[] 大小
 *    有8个，那么，ConcurrentHashMap的大小就是8*4=32。
 *    无论是Segment[] 还是HashEntry[] 初始化容量或者扩容，其大小都要不小于2的幂次方的值。（向上取整）
 *
 *
 *    2.一个Segment存的HashEntry[]  默认最小容量是 2；
 *
 *    3.ConcurrentHashMap默认的Segment数组默认长度是16；此时，ConcurrentHashMap的容量大小是2*16=32，既默认大小是 32。
 *
 *
 *    4.为什么Segment[0] 在初始化时候就已经创建好了，这里是一种比较好的优化策略，因为，创建一个Segment的时候，其他Segment对象需要的参数和Segment基本是一样的，
 *    比如：初始化容量 cap,loadFactor,threshold , 等都是计算好的，直接使用就好，因为创建初始化参数也需要计算，和HashMap的扩容一个道理。创建Segment是一个单例模式，而且是
 *    DCL（Double Check Locking ）双检查锁机制+CAS(Compare And  Swap）来保证并发情况下的安全性。
 *
 *    5.ConcurrentHashMap put()的时候，为了保证多线程的安全性，使用tryLock()来实现加锁。被阻塞时候可以做其他事情：1.遍历链表，2、new Entry(key,value)  ,3、
 *
 *
 *   JDK 1.8的时候时候已经摒弃了分段锁Segment的概念，而是用了 Node 数组+链表+红⿊树的数据结构来实现，并发控制使⽤ synchronized 和
 * CAS 来操作。
 *
 *     ConcurrentHashMap取消了Segment分段锁，采⽤CAS和synchronized来保证并发安全。数据结构跟
 *     HashMap1.8的结构类似，数组+链表/红⿊⼆叉树。Java 8在链表⻓度超过⼀定阈值（8）时将链表（寻
 *    址时间复杂度为O(N)）转换为红⿊树（寻址时间复杂度为O(log(N))）
 *    synchronized只锁定当前链表或红⿊⼆叉树的⾸节点，这样只要hash不冲突，就不会产⽣并发，效率⼜
 *    提升N倍。
 *
 */
public class CurrentHashMap_Study {
    @Test
    public void test(){
        ConcurrentHashMap<String,String>  concurrentHashMap=new ConcurrentHashMap<>();
        concurrentHashMap.put("abc1","123");
        concurrentHashMap.put("abc2","123");
        concurrentHashMap.put("abc3","123");
        concurrentHashMap.put("abc4","123");


    }
}
